/** @file    WoshObjectTreeModel.cpp
 *  @author  Alessandro Polo
 *  @version $Id: WoshObjectTreeModel.cpp 2808 2010-07-28 20:44:00Z alex $
 *  @brief
 * File containing methods for the wosh::gui::WoshObjectTreeModel class.
 * The header for this class can be found in WoshObjectTreeModel.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "WoshObjectTreeModel.h"

 #include <core/Table.h>
 #include <core/Variant.h>
 #include <core/List.h>
 #include <framework/gui/GuiUtilities.h>


using namespace std;
namespace wosh {
 namespace gui {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WoshObjectTreeModel::WoshObjectTreeModel( WoshObject* root_item, QObject *parent ) : QAbstractItemModel(parent) {
	this->rootItem = root_item;
	this->fetchMoreEnabled = false;
	updated();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WoshObjectTreeModel::updated() {
	if ( this->rootItem == NULL ) return;
	reset();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int WoshObjectTreeModel::rowCount( const QModelIndex &parent ) const {
	WoshObject *parentItem = NULL;
	if ( !parent.isValid() ) parentItem = this->rootItem;
	else
		parentItem = static_cast<WoshObject*>(parent.internalPointer());
	if ( parentItem == NULL )
		return 0;
	return parentItem->getChildren().size();
 }

int WoshObjectTreeModel::columnCount( const QModelIndex &parent ) const {
	(void)parent;
	return 3;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

QModelIndex WoshObjectTreeModel::index( int row, int column, const QModelIndex &parent ) const {
	if ( !hasIndex(row, column, parent) ) return QModelIndex();
	WoshObject *parentItem = NULL;
	if (!parent.isValid()) parentItem = this->rootItem;
	else parentItem = static_cast<WoshObject*>(parent.internalPointer());
	WoshObject *childItem = parentItem->getChildren().itemAt(row);
	if (childItem) return createIndex(row, column, childItem);
	else return QModelIndex();
 }

QModelIndex WoshObjectTreeModel::parent( const QModelIndex &child ) const {
	if ( !child.isValid() ) return QModelIndex();
	WoshObject *childItem = static_cast<WoshObject*>(child.internalPointer());
	if ( childItem == NULL ) return QModelIndex();
	WoshObject *parentItem = childItem->getParent();
	if (parentItem == this->rootItem) return QModelIndex();
	if (parentItem == NULL) return QModelIndex();
	WoshObject *parentParentItem = parentItem->getParent();
	if ( parentParentItem == NULL )
		return createIndex(1,0, parentItem);
	int childIndex = parentParentItem->getChildren().indexOf(parentItem);
	if( childIndex < 0 ) childIndex = 0;
	return createIndex(childIndex,0, parentItem);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

QVariant WoshObjectTreeModel::data( const QModelIndex &index, int role ) const {
	if ( this->rootItem == NULL || !index.isValid() ) return QVariant();
	WoshObject *item = static_cast<WoshObject*>(index.internalPointer());
	if ( item == NULL ) return QVariant();

	if ( role == Qt::DisplayRole ) {
		switch (index.column()) {
			case 0:		return QVariant( item->getRemoteName().c_str() );
			case 1:		return QVariant( item->getRemoteType().c_str() );
			case 2:		return QVariant( item->getRemoteURI().toString().c_str() );
			default:	return QVariant();
		 }
		return QVariant();
	 }
	else if ( role == Qt::ToolTipRole ) {
		return QVariant();
	 }
	else if ( index.column() == 0 && role == Qt::DecorationRole ) {
		return QVariant( GuiUtilities::createIconFor(item->getRemoteType()) );
	 }
	return QVariant();
 }

QVariant WoshObjectTreeModel::headerData( int section, Qt::Orientation orientation, int role ) const {
	if (role != Qt::DisplayRole) return QVariant();
	if (orientation == Qt::Horizontal) {
		 switch(section) {
			case 0:		return QString("Name");
			case 1:		return QString("Type");
			case 2:		return QString("URI");
		  }
		 return QString("Col%1").arg(section);
	 }
	return QVariant();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Qt::ItemFlags WoshObjectTreeModel::flags( const QModelIndex &index ) const {
	if ( !index.isValid() ) return 0;
	return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool WoshObjectTreeModel::canFetchMore( const QModelIndex & parent ) const 
 {
	if ( !this->fetchMoreEnabled )
		return false;
	WoshObject* item = static_cast<WoshObject*>(parent.internalPointer());
	if ( item == NULL )
		return false;
	if ( Utilities::std_time() - item->getUpdated() > 40 )
		return true;
	return false;
 }

void WoshObjectTreeModel::fetchMore( const QModelIndex & parent )
 {
	WoshObject* item = static_cast<WoshObject*>(parent.internalPointer());
	if ( item == NULL )
		return;
	emit s_requestChildrenOf( item );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

QModelIndex WoshObjectTreeModel::getIndexOf( const wosh::URI& uri, const QModelIndex &parent )
 {
	int rows = rowCount(parent);
	for(int row=0; row<rows; row++) {
		QModelIndex inx = index( row, 0, parent );
		if ( !inx.isValid() ) continue;
		WoshObject* item = static_cast<WoshObject*>(inx.internalPointer());
		if ( item == NULL ) continue;

		if ( item->getRemoteURI() == uri )
			return inx;

		inx = getIndexOf( uri, inx );
		if ( inx.isValid() )
			return inx;
	 }
	return QModelIndex();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void WoshObjectTreeModel::updateChildrenOf( const wosh::Table* table, WoshObject* objParent )
 {
	if ( objParent == NULL ) return;
	vector<WoshObject*> children = objParent->getChildren().getVector();
	unsigned int oldcount = children.size();
	updateObjectsFromTable( table, objParent, children, NULL );
	if ( children.size() == oldcount )
		return;
	QModelIndex ind = getIndexOf( objParent->getRemoteURI() );
	if ( oldcount < children.size() ) {
		beginInsertRows(ind, oldcount, children.size()-oldcount);
		std::vector<WoshObject*>::iterator it;
		for ( it=children.begin(); it!=children.end(); ++it ) {
			(*it)->setParent(objParent, true);
		 }
		endInsertRows();
	 }
	else {
		// remove
	 }
 }

void WoshObjectTreeModel::updateChildren( const wosh::Table* table, const wosh::URI& parent_uri )
 {
	WoshObject* objParent = findChildOf( parent_uri, this->rootItem );
	if ( objParent == NULL ) return;
	vector<WoshObject*> children = objParent->getChildren().getVector();
	unsigned int oldcount = children.size();
	updateObjectsFromTable( table, objParent, children, NULL );
	if ( children.size() == oldcount )
		return;
	///@bug critical bug from here!
	QModelIndex ind = getIndexOf( parent_uri );
	if ( oldcount < children.size() ) {
		beginInsertRows(ind, oldcount, children.size()-oldcount);
		std::vector<WoshObject*>::iterator it;
		for ( it=children.begin(); it!=children.end(); ++it ) {
			(*it)->setParent(objParent, true);
		 }
		endInsertRows();
	 }
	else {
		// remove
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int updateChildrenOf( const wosh::Table* table, WoshObject* objParent )
 {
	if ( objParent == NULL )
		return -1;
	return updateObjectsFromTable( table, objParent );
 }

int updateChildren( const wosh::Table* table, const wosh::URI& parent_uri, WoshObject* root )
 {
	WoshObject* objParent = findChildOf( parent_uri, root );
	if ( objParent == NULL )
		return -1;
	objParent->setUpdated();
	return updateObjectsFromTable( table, objParent );
 }


WoshObject* findChildOf( const wosh::URI& uri, WoshObject* parent )
 {
	if ( parent == NULL ) return NULL;
	std::vector<WoshObject*>::const_iterator it;
	std::vector<WoshObject*>::const_iterator it_end = parent->getChildren().end();
	for ( it=parent->getChildren().begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( (*it)->getRemoteURI() == uri )
			return *it;
		WoshObject* obj = findChildOf(uri, *it);
		if ( obj != NULL )
			return obj;
	 }
	return NULL;
 }

int updateObjectsFromTable( const wosh::Table* table, WoshObject* parent, WoshObject* newTemplate )
 {
	if ( parent == NULL ) return -2;
	return updateObjectsFromTable( table, parent, parent->getChildren().getVector(), newTemplate );
 }

int updateObjectsFromTable( const wosh::Table* table, WoshObject* parent, vector<WoshObject*>& children, WoshObject* newTemplate )
 {
	if ( table == NULL ) return -1;
	unsigned int rows = table->getRows();
	unsigned int cols = table->getCols();
	int colNameIndex = -1;
	int colTypeIndex = -1;
	int colInterfacesIndex = -1;
	int colURIIndex = -1;

	if ( table->hasHeaderHorizontal() ) {
		for (unsigned int col=0; col<cols; col++) {
			const ISerializable* obj = table->getHeaderHorizontal(col);
			if ( obj == NULL || !obj->isKindOf<Variant>() )
				continue;
			const wosh::Variant* dataFields = dynamic_cast<const wosh::Variant*>(obj);
			if ( dataFields == NULL ) continue;
			if ( *dataFields == "Name" )
				colNameIndex = col;
			else if ( *dataFields == "Type" )
				colTypeIndex = col;
			else if ( *dataFields == "Interfaces" )
				colInterfacesIndex = col;
			else if ( *dataFields == "URI" )
				colURIIndex = col;
		 }
	}

	if ( colNameIndex == -1 && colURIIndex == -1 ) {
		return -1;
	 }
	if ( colNameIndex == -1 )
		colNameIndex = colURIIndex;

	int obj_updated = 0;
	int obj_errors = 0;

	for (unsigned int row=0; row<rows; row++) {
		const ISerializable* obj = table->get(row, colNameIndex);
		if ( obj == NULL || !obj->isKindOf<Variant>() ) {
			++obj_errors;
			continue;
		 }
		const wosh::Variant* data = dynamic_cast<const wosh::Variant*>(obj);
		if ( data == NULL || data->isEmpty() ) {
			++obj_errors;
			continue;
		 }

		WoshObject* cObj = NULL;
		std::vector<WoshObject*>::iterator it;
		for ( it=parent->getChildren().begin(); it!=parent->getChildren().end(); ++it ) {
			if ( *it == NULL ) continue;
			if ( (*it)->getRemoteName() != data->toString() ) continue;
			cObj = *it;
			break;
		 }
		if ( cObj == NULL ) {
			if ( newTemplate == NULL ) 
				cObj = new WoshObject();
			else
				cObj = newTemplate->clone();
			children.push_back(cObj);
			cObj->setParent(parent, true);
			cObj->setRemoteName(data->toString());
		 }

		if ( colURIIndex != -1 ) {
			const ISerializable* objURI = table->get(row, colURIIndex);
			if ( objURI != NULL && objURI->isKindOf<Variant>() ) {
				const wosh::Variant* dataURI = dynamic_cast<const wosh::Variant*>(objURI);
				cObj->setRemoteURI( URI(dataURI->toString()) );
			 }
			else if ( objURI != NULL && objURI->isKindOf<URI>() ) {
				const wosh::URI* uriURI = dynamic_cast<const wosh::URI*>(objURI);
				cObj->setRemoteURI( *uriURI );
			 }
			else ++obj_errors;
		 }
	//	else if ( newTemplate != NULL ) { // guess..
	//		URI uri = newTemplate->getRemoteURI();
	//		uri->setName( cObj->getRemoteName() );
	//		cObj->setRemoteURI( uri );
	//	 }
		else ++obj_errors;

		if ( colInterfacesIndex != -1 ) {
			const ISerializable* objInterfaces = table->get(row, colInterfacesIndex);
			if ( objInterfaces != NULL && objInterfaces->isKindOf<List>() ) {
				const wosh::List* dataInterfaces = dynamic_cast<const wosh::List*>(objInterfaces);
				cObj->getInterfaces().clear();
				for(int j=0; j<dataInterfaces->size(); j++) {
					const Variant* vData = dynamic_cast<const Variant*>(dataInterfaces->itemAt(j));
					if ( vData == NULL ) continue;
					cObj->getInterfaces().append( vData->toString().c_str() );
				 }
			 }
			else if ( objInterfaces != NULL && objInterfaces->isKindOf<Variant>() ) {
				const wosh::Variant* dataInterfaces = dynamic_cast<const wosh::Variant*>(objInterfaces);
				cObj->getInterfaces().clear();
				vector<string> inters;
				Utilities::splitString(dataInterfaces->toString(), ";", inters, false, true);
				for(unsigned int j=0; j<inters.size(); j++) {
					cObj->getInterfaces().append( inters.at(j).c_str() );
				 }
			 }
			else ++obj_errors;
		 }

		if ( colTypeIndex != -1 ) {
			const ISerializable* objType = table->get(row, colTypeIndex);
			if ( objType != NULL && objType->isKindOf<Variant>() ) {
				const wosh::Variant* dataType = dynamic_cast<const wosh::Variant*>(objType);
				cObj->setRemoteType( dataType->toString() );
			 }
			else ++obj_errors;
		 }
		else if ( cObj->getInterfaces().size() > 0 ) {
			// by default last interface entry is type (when type missing we may recover from this)
			cObj->setRemoteType( cObj->getInterfaces().last().toLatin1().data() );
		 }

		++obj_updated;
	 }

	if ( parent != NULL )
		parent->setUpdated();

	if ( obj_updated == 0 )
		return -obj_errors;

	return obj_updated;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace gui
}; // namespace wosh
